package com.boarsoft.hibernate;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.NestedTransactionNotSupportedException;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionSuspensionNotSupportedException;
import org.springframework.transaction.annotation.Transactional;

/**
 * 自定义的@Transactional注解处理
 * 
 * @author Mac_J
 *
 */
public class SimpleHibernateTxAdviceImpl {
	protected SessionFactory sessionFactory;

	public Object doAround(ProceedingJoinPoint jp) throws Throwable {
		Object target = jp.getTarget();
		Class<?> tc = target.getClass();
		Signature s = jp.getSignature();
		MethodSignature ms = (MethodSignature) s;
		Method method = tc.getMethod(ms.getName(), ms.getParameterTypes());
		//
		Transactional ta = null;
		if (method.isAnnotationPresent(Transactional.class)) {
			ta = method.getAnnotation(Transactional.class);
		}
		if (ta == null) {
			if (tc.isAnnotationPresent(Transactional.class)) {
				ta = tc.getAnnotation(Transactional.class);
			} else {
				// 如果没有配置事务，由方法自己通过SimpleHibernateSessionFactory获取session
				// 如果外层方法有开启事务，则当前线程已经绑定了session，可以被获取到
				return jp.proceed();
			}
		}
		return this.proceed(ta, jp);
	}

	protected Object proceed(Transactional ta, ProceedingJoinPoint jp) throws Throwable {
		// 此时ta不可能是null，表示此方法有配置事务，检查外层方法是否已开启事务
		boolean isNewBind = false;
		HibernateSessionTx st = HibernateSessionTx.get();
		if (st == null) {
			st = new HibernateSessionTx();
			HibernateSessionTx.set(st);
			isNewBind = true;
		}
		try {
			return this.proceed(ta, jp, st);
		} finally {
			if (isNewBind) {
				HibernateSessionTx.remove();
			}
		}
	}

	protected Object proceed(Transactional ta, ProceedingJoinPoint jp, HibernateSessionTx st) throws Throwable {
		boolean isNewSession = false;
		Session ss = st.getSession();
		Transaction tx = st.peekTransaction();
		if (ss == null) {
			ss = sessionFactory.openSession();
			st.setSession(ss);
			isNewSession = true;
		}
		try {
			return this.proceed(ta, jp, st, ss, tx);
		} finally {
			if (isNewSession && ss != null) {
				ss.close();
			}
		}
	}

	protected Object proceed(Transactional ta, ProceedingJoinPoint jp, HibernateSessionTx st, Session ss,
			Transaction tx) throws Throwable {
		switch (ta.propagation()) {
		case NESTED:
			// 如果一个活动的事务存在，则运行在一个嵌套的事务中，如果没有活动事务，则等同于REQUIRED
			if (tx != null) {
				// TODO 开启内嵌事务？
				throw new NestedTransactionNotSupportedException(String.format(//
						"method = %s", jp.toString()));
			}
		case REQUIRED:
			// 如果存在一个事务，则支持当前事务。如果没有事务则开启。
			if (tx == null) {
				tx = ss.beginTransaction();
				st.pushTransaction(tx);
				try {
					Object r = jp.proceed();
					tx.commit();
					return r;
				} catch (Exception e) {
					if (tx != null) {
						tx.rollback();
					}
					throw e;
				} finally {
					st.popTransaction();
				}
			} else {
				return jp.proceed();
			}
		case SUPPORTS:
			// 如果存在一个事务，支持当前事务。如果没有事务，则非事务的执行。
			return jp.proceed();
		case MANDATORY:
			// 如果已经存在一个事务，支持当前事务。如果没有一个活动的事务，则抛出异常。
			if (tx == null) {
				throw new NoTransactionException(String.format(//
						"method = %s", jp.toString()));
			}
			return jp.proceed(); // 因为事务已存在，不用再开启
		case REQUIRES_NEW:
			// 总是开启一个新的事务。如果一个事务存在，则将这个存在的事务挂起。
			try {
				tx = ss.beginTransaction();
				st.pushTransaction(tx);
				try {
					Object r = jp.proceed();
					tx.commit();
					return r;
				} finally {
					st.popTransaction();
				}
			} catch (Exception e) {
				if (tx != null) {
					tx.rollback();
				}
				throw e;
			}
		case NOT_SUPPORTED:
			// 总是非事务地执行，并挂起任何存在的事务。
			if (tx == null) {
				return jp.proceed();
			}
			// TODO 在新的会话上开启新事务以挂起当前事务？
			throw new TransactionSuspensionNotSupportedException(String.format(//
					"method = %s", jp.toString()));
		case NEVER:
			// 总是非事务地执行，如果存在一个活动事务，则抛出异常。
			if (tx == null) {
				return jp.proceed();
			}
			throw new IllegalTransactionStateException(String.format(//
					"method = %s", jp.toString()));
		default:
			throw new IllegalTransactionStateException(String.format(//
					"Unsupported propagation on method = %s", jp.toString()));
		}
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
}